home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-13 / dte5_1.zip / BLOCK.C < prev    next >
C/C++ Source or Header  |  1991-02-06  |  39KB  |  1,231 lines

  1. /*
  2.  * Written by Douglas Thomson (1989/1990)
  3.  *
  4.  * This source code is released into the public domain.
  5.  */
  6.  
  7. /*
  8.  * Name:    dte - Doug's Text Editor program - block commands module
  9.  * Purpose: This file contains all the commands than manipulate blocks.
  10.  * File:    block.c
  11.  * Author:  Douglas Thomson
  12.  * System:  this file is intended to be system-independent
  13.  * Date:    October 1, 1989
  14.  */
  15.  
  16. #ifdef HPXL
  17. #include "commonh"
  18. #include "utilsh"
  19. #include "blockh"
  20. #else
  21. #include "common.h"
  22. #include "utils.h"
  23. #include "block.h"
  24. #endif
  25.  
  26. /*
  27.  * prototypes for all functions in this file
  28.  */
  29. void set_marker ARGS((windows *window, int n));
  30. void mark_start ARGS((windows *window));
  31. void mark_end ARGS((windows *window));
  32. void prepare_block ARGS((windows *window));
  33. void block_move ARGS((windows *window));
  34. void block_copy ARGS((windows *window));
  35. void block_read ARGS((windows *window, int fixup));
  36. int check_block ARGS((windows *window));
  37. void block_delete ARGS((windows *window));
  38. void block_indent ARGS((windows *window));
  39. void block_unindent ARGS((windows *window));
  40. void block_write ARGS((windows *window));
  41. void block_print ARGS((windows *window));
  42.  
  43. /*
  44.  * Name:    set_marker
  45.  * Purpose: To record the position of a marker in the file.
  46.  * Date:    October 1, 1989
  47.  * Passed:  window: information required to access current window
  48.  *          n:      the number of the marker to set
  49.  * Notes:   n must be in the range 0..9
  50.  */
  51. void set_marker(window, n)
  52. windows *window;
  53. int n;
  54. {
  55.     int col;    /* cursor column, or end of line, whichever is less */
  56.     int len;    /* length of current line */
  57.  
  58.     if (g_status.copied) {
  59.         /*
  60.          * current line buffer is active, so set the buffer marker
  61.          */
  62.         if ((col = window->ccol) > (len = linelen(g_status.line_buff))) {
  63.             /*
  64.              * markers can only be placed where there is actually some
  65.              *  text. If the cursor is beyond the end of the line, then
  66.              *  the marker must be set at the end of the line
  67.              */
  68.             col = len;
  69.         }
  70.         g_status.buff_marker[n] = g_status.line_buff + col;
  71.     }
  72.     else {
  73.         /*
  74.          * work directly with main text
  75.          */
  76.         if ((col = window->ccol) > (len = linelen(window->cursor))) {
  77.             col = len;
  78.         }
  79.         window->file_info->marker[n] = window->cursor + col;
  80.     }
  81. }
  82.  
  83. /*
  84.  * Name:    mark_start
  85.  * Purpose: To record the position of the start of the block in the file.
  86.  * Date:    October 1, 1989
  87.  * Passed:  window: information required to access current window
  88.  * Notes:   This differs slightly from the setting of a normal mark,
  89.  *           in that if we are using the current line buffer, the main
  90.  *           text start marker must be modified also. (This may not
  91.  *           still be necessary?)
  92.  */
  93. void mark_start(window)
  94. windows *window;
  95. {
  96.     int col;    /* cursor column, or end of line, whichever is less */
  97.     int len;    /* length of current line */
  98.  
  99.     /*
  100.      * if the end of the block has already been marked, then the block
  101.      *  must now become visible
  102.      */
  103.     if (window->file_info->marker[END_BLOCK]) {
  104.         window->file_info->visible = TRUE;
  105.     }
  106.  
  107.     if (g_status.copied) {
  108.         if ((col = window->ccol) > (len = linelen(g_status.line_buff))) {
  109.             col = len;
  110.         }
  111.         window->file_info->marker[START_BLOCK] = window->cursor;
  112.         g_status.buff_marker[START_BLOCK] = g_status.line_buff + col;
  113.     }
  114.     else {
  115.         if ((col = window->ccol) > (len = linelen(window->cursor))) {
  116.             col = len;
  117.         }
  118.         window->file_info->marker[START_BLOCK] = window->cursor + col;
  119.     }
  120. }
  121.  
  122. /*
  123.  * Name:    mark_end
  124.  * Purpose: To record the position of the end of the block in the file.
  125.  * Date:    October 1, 1989
  126.  * Passed:  window: information required to access current window
  127.  * Notes:   This differs slightly from the setting of a normal mark,
  128.  *           in that if we are using the current line buffer, the main
  129.  *           text end marker must be modified also.
  130.  */
  131. void mark_end(window)
  132. windows *window;
  133. {
  134.     int col;    /* cursor column, or end of line, whichever is less */
  135.     int len;    /* length of current line */
  136.  
  137.     if (window->file_info->marker[START_BLOCK]) {
  138.         window->file_info->visible = TRUE;
  139.     }
  140.     if (g_status.copied) {
  141.         if ((col = window->ccol) > (len = linelen(g_status.line_buff))) {
  142.             col = len;
  143.         }
  144.         window->file_info->marker[END_BLOCK] = window->cursor;
  145.         g_status.buff_marker[END_BLOCK] = g_status.line_buff + col;
  146.     }
  147.     else {
  148.         if ((col = window->ccol) > (len = linelen(window->cursor))) {
  149.             col = len;
  150.         }
  151.         window->file_info->marker[END_BLOCK] = window->cursor + col;
  152.     }
  153. }
  154.  
  155. /*
  156.  * Name:    prepare_block
  157.  * Purpose: To prepare a window/file for a block read, move or copy.
  158.  * Date:    October 10, 1989
  159.  * Passed:  window: information required to access current window
  160.  * Notes:   The main complication is that the cursor may be beyond the end
  161.  *           of the current line, in which case extra padding spaces have
  162.  *           to be added before the block operation can take place.
  163.  */
  164. void prepare_block(window)
  165. windows *window;
  166. {
  167.     text_ptr source;        /* source for block moves */
  168.     text_ptr dest;          /* destination for block moves */
  169.     int pad;                /* amount of padding to be added */
  170.     int len;                /* length (usually of current line) */
  171.     int cr;                 /* does current line end with \n? */
  172.     long number;            /* number of characters for block moves */
  173.  
  174.     /*
  175.      * if cursor was beyond the end of the actual line, then add
  176.      *  padding first.
  177.      *
  178.      * work on the current line buffer until padding is sorted out.
  179.      */
  180.     copy_line(window);
  181.     if (window->ccol > (len = linelen(g_status.line_buff))) {
  182.         /*
  183.          * work out how much padding is required to extend the current
  184.          *  line to the cursor position
  185.          */
  186.         if (g_status.line_buff[len] == '\n') {
  187.             cr = 1;
  188.         }
  189.         else {
  190.             cr = 0;
  191.         }
  192.         pad = window->ccol - len;
  193.  
  194.         /*
  195.          * check that there is room in the current line - should never
  196.          *  give any problems...
  197.          */
  198.         if (len + pad + cr >= BUFF_SIZE) {
  199.             error(WARNING, "too far right");
  200.             return;
  201.         }
  202.  
  203.         /*
  204.          * make room for the padding spaces, and fix the markers
  205.          */
  206.         source = g_status.line_buff + window->ccol - pad;
  207.         dest = source + pad;
  208.         number = len + pad - window->ccol + 1 + cr;
  209.         hw_move(dest, source, number);
  210.         fix_marks(window, source, (long) pad);
  211.  
  212.         /*
  213.          * insert the padding spaces
  214.          */
  215.         while (pad--) {
  216.             *source++ = ' ';
  217.         }
  218.     }
  219.     un_copy_line(window);
  220.  
  221.     /*
  222.      * The cursor is now somewhere in the main text block, so there
  223.      *  are no major complications.
  224.      */
  225. }
  226.  
  227. /*
  228.  * Name:    move_block
  229.  * Purpose: To move the marked block from its current position to the
  230.  *           cursor position.
  231.  * Date:    October 1, 1989
  232.  * Passed:  window: information required to access current window
  233.  */
  234. void block_move(window)
  235. windows *window;
  236. {
  237.     text_ptr source;        /* source for block moves */
  238.     text_ptr dest;          /* destination for block moves */
  239.     long number;            /* number of characters for block moves */
  240.     int len;                /* length (usually of current line) */
  241.     int add;                /* characters being added from another line */
  242.     long block_len;         /* length of the block */
  243.     text_ptr orig;          /* cursor location in text */
  244.     text_ptr block_text;    /* place block is temporarily moved to */
  245.     text_ptr block_dest;    /* place block must finish up in */
  246.     text_ptr fix_pos;       /* origin for marker fixup */
  247.  
  248.     if (!window->file_info->visible) {
  249.         error(WARNING, "no visible block");
  250.         return;
  251.     }
  252.  
  253.     /*
  254.      * if cursor was beyond the end of the actual line, then add
  255.      *  padding first
  256.      */
  257.     prepare_block(window);
  258.  
  259.     /*
  260.      * make sure block is legitimate
  261.      */
  262.     if (window->file_info->marker[END_BLOCK] <=
  263.             window->file_info->marker[START_BLOCK]) {
  264.         error(WARNING, "end before start");
  265.         return;
  266.     }
  267.  
  268.     /*
  269.      * work out how much has to be moved
  270.      */
  271.     block_len = window->file_info->marker[END_BLOCK] -
  272.             window->file_info->marker[START_BLOCK];
  273.  
  274.     /*
  275.      * now check that no lines will become too long as a result of
  276.      *  the block move
  277.      *
  278.      * first check that the text on the cursor line up to the cursor, plus
  279.      *  the text on the line the block starts on from the start of the
  280.      *  block to the end of the line, will all fit on one line
  281.      */
  282.     len = linelen(window->cursor);
  283.     add = linelen(window->file_info->marker[START_BLOCK]);
  284.     if (window->file_info->marker[START_BLOCK] + add >
  285.             window->file_info->marker[END_BLOCK]) {
  286.         add = (int) (window->file_info->marker[END_BLOCK] -
  287.                 window->file_info->marker[START_BLOCK]);
  288.     }
  289.     if (window->file_info->marker[START_BLOCK][add] == '\n') {
  290.         ++add;
  291.     }
  292.     if (window->ccol + add >= BUFF_SIZE) {
  293.         error(WARNING, "line would be too long");
  294.         return;
  295.     }
  296.  
  297.     /*
  298.      * next check that the text on the cursor line from the cursor to the
  299.      *  end of the line, plus the text on the line the block ends on from the
  300.      *  start of the line to the end of the block, will all fit on one line
  301.      */
  302.     add = prelinelen(window->file_info->marker[END_BLOCK]);
  303.     if (window->file_info->marker[START_BLOCK] + add >
  304.             window->file_info->marker[END_BLOCK]) {
  305.         /*
  306.          * if the block is smaller than one line, then we only need to worry
  307.          *  about the text actually in the block
  308.          */
  309.         add = (int) (window->file_info->marker[END_BLOCK] -
  310.                 window->file_info->marker[START_BLOCK]);
  311.     }
  312.     if (len - window->ccol + add >= BUFF_SIZE) {
  313.         error(WARNING, "line would be too long");
  314.         return;
  315.     }
  316.  
  317.     /*
  318.      * finally check that the text on the line the block starts on from the
  319.      *  start of the line to the start of the block, plus the text on the line
  320.      *  the block ends on from the end of the block to the end of the line,
  321.      *  will all fit on one line
  322.      */
  323.     add = linelen(window->file_info->marker[END_BLOCK]);
  324.     if (window->file_info->marker[END_BLOCK][add] == '\n') {
  325.         ++add;
  326.     }
  327.     if (prelinelen(window->file_info->marker[START_BLOCK]) + add >=
  328.             BUFF_SIZE) {
  329.         error(WARNING, "line would be too long");
  330.         return;
  331.     }
  332.  
  333.     /*
  334.      * check that the move is going to have some effect
  335.      */
  336.     orig = window->cursor + window->ccol;
  337.     if (orig >= window->file_info->marker[START_BLOCK] &&
  338.             orig <= window->file_info->marker[END_BLOCK]) {
  339.         /*
  340.          * a block moved to within the block itself has no effect
  341.          */
  342.         return;
  343.     }
  344.  
  345.     /*
  346.      * check that there is room to copy the block to the end of the
  347.      *  text buffer
  348.      */
  349.     if (g_status.end_mem + block_len >= g_status.max_mem) {
  350.         error(WARNING, "not enough memory for move");
  351.         return;
  352.     }
  353.  
  354.     /*
  355.      * make copy of block after the end of the main text
  356.      */
  357.     source = window->file_info->marker[START_BLOCK];
  358.     block_text = g_status.end_mem + 1;
  359.     hw_move(block_text, source, block_len);
  360.  
  361.     /*
  362.      * shift the text between the cursor and the block to make room
  363.      */
  364.     if (orig < window->file_info->marker[START_BLOCK]) {
  365.         source = orig;
  366.         fix_pos = window->file_info->marker[START_BLOCK] + block_len;
  367.         dest = source + block_len;
  368.         block_dest = source;
  369.         number = window->file_info->marker[START_BLOCK] - source;
  370.     }
  371.     else {
  372.         source = window->file_info->marker[END_BLOCK];
  373.         dest = window->file_info->marker[START_BLOCK];
  374.         fix_pos = window->file_info->marker[START_BLOCK];
  375.         block_dest = orig - block_len;
  376.         number = orig - source;
  377.     }
  378.     hw_move(dest, source, number);
  379.  
  380.     /*
  381.      * if the cursor used to be after the block, it is now before it
  382.      */
  383.     if (window->cursor > window->file_info->marker[END_BLOCK]) {
  384.         window->cursor -= block_len;
  385.     }
  386.  
  387.     /*
  388.      * move the block back into the gap where it belongs
  389.      */
  390.     hw_move(block_dest, block_text, block_len);
  391.  
  392.     /*
  393.      * fix up all the affected markers. Note that markers within the
  394.      *  block are not moved with the block, but instead all converge
  395.      *  at where the block used to start. Here is the place to fix this
  396.      *  if it is ever a problem.
  397.      */
  398.     fix_marks(window, orig, block_len);
  399.     fix_marks(window, fix_pos, -block_len);
  400.  
  401.     /*
  402.      * the marked block is now in a new place
  403.      */
  404.     window->file_info->marker[START_BLOCK] = block_dest;
  405.     window->file_info->marker[END_BLOCK] = block_dest + block_len;
  406. }
  407.  
  408. /*
  409.  * Name:    copy_block
  410.  * Purpose: To copy the marked block from its current position to the
  411.  *           cursor position.
  412.  * Date:    October 1, 1989
  413.  * Passed:  window: information required to access current window
  414.  * Notes:   This operation is complicated by the fact that the block
  415.  *           may be copied to within itself. An extra copy of the block
  416.  *           is therefore made, just to make life easier for the
  417.  *           programmer!
  418.  */
  419. void block_copy(window)
  420. windows *window;
  421. {
  422.     text_ptr source;        /* source for block moves */
  423.     text_ptr dest;          /* destination for block moves */
  424.     long number;            /* number of characters for block moves */
  425.     int len;                /* length (usually of current line) */
  426.     int add;                /* characters being added from another line */
  427.     long block_len;         /* length of the block */
  428.     text_ptr orig;          /* cursor location in text */
  429.     text_ptr block_text;    /* place block is temporarily moved to */
  430.     text_ptr block_dest;    /* place block must finish up in */
  431.  
  432.     if (!window->file_info->visible) {
  433.         error(WARNING, "no visible block");
  434.         return;
  435.     }
  436.  
  437.     /*
  438.      * if cursor was beyond the end of the actual line, then add
  439.      *  padding first
  440.      */
  441.     prepare_block(window);
  442.  
  443.     if (window->file_info->marker[END_BLOCK] <=
  444.             window->file_info->marker[START_BLOCK]) {
  445.         error(WARNING, "end before start");
  446.         return;
  447.     }
  448.  
  449.     block_len = window->file_info->marker[END_BLOCK] -
  450.             window->file_info->marker[START_BLOCK];
  451.  
  452.     /*
  453.      * now check that no lines will become too long as a result of
  454.      *  the block move
  455.      */
  456.     len = linelen(window->cursor);
  457.     add = linelen(window->file_info->marker[START_BLOCK]);
  458.     if (window->file_info->marker[START_BLOCK] + add >
  459.             window->file_info->marker[END_BLOCK]) {
  460.         add = (int) (window->file_info->marker[END_BLOCK] -
  461.                 window->file_info->marker[START_BLOCK]);
  462.     }
  463.     if (window->file_info->marker[START_BLOCK][add] == '\n') {
  464.         ++add;
  465.     }
  466.     if (window->ccol + add >= BUFF_SIZE) {
  467.         error(WARNING, "line would be too long");
  468.         return;
  469.     }
  470.     add = prelinelen(window->file_info->marker[END_BLOCK]);
  471.     if (window->file_info->marker[START_BLOCK] + add >
  472.             window->file_info->marker[END_BLOCK]) {
  473.         add = (int) (window->file_info->marker[END_BLOCK] -
  474.                 window->file_info->marker[START_BLOCK]);
  475.     }
  476.     if (len - window->ccol + add >= BUFF_SIZE) {
  477.         error(WARNING, "line would be too long");
  478.         return;
  479.     }
  480.  
  481.     orig = window->cursor + window->ccol;
  482.  
  483.     /*
  484.      * the block is copied to the end of the file, then all the
  485.      *  text is shifted to make room for the copy of the block,
  486.      *  then the block is moved to its new position. This means we
  487.      *  need room for 2 extra copies of the block in memory.
  488.      */
  489.     if (g_status.end_mem + 2*block_len >= g_status.max_mem) {
  490.         error(WARNING, "not enough memory for copy");
  491.         return;
  492.     }
  493.  
  494.     /*
  495.      * make copy of block after where text will finally end
  496.      */
  497.     source = window->file_info->marker[START_BLOCK];
  498.     block_text = g_status.end_mem + block_len;
  499.     hw_move(block_text, source, block_len);
  500.  
  501.     /*
  502.      * move text to make room for block
  503.      */
  504.     source = orig;
  505.     dest = source + block_len;
  506.     block_dest = source;
  507.     number = g_status.end_mem - source;
  508.     hw_move(dest, source, number);
  509.  
  510.     /*
  511.      * put block in the hole
  512.      */
  513.     hw_move(block_dest, block_text, block_len);
  514.  
  515.     /*
  516.      * adjust all the marks
  517.      */
  518.     fix_marks(window, orig, block_len);
  519.  
  520.     /*
  521.      * the block is now somewhere new
  522.      */
  523.     window->file_info->marker[START_BLOCK] = block_dest;
  524.     window->file_info->marker[END_BLOCK] = block_dest + block_len;
  525. }
  526.  
  527. /*
  528.  * Name:    block_read
  529.  * Purpose: To read a file into the text at the cursor position, marking
  530.  *           this text as the current block. Optionally, tabs may be
  531.  *           expanded and the file checked for printable ASCII characters
  532.  *           only.
  533.  * Date:    October 1, 1989
  534.  * Passed:  window: information required to access current window
  535.  *          fixup:  should we expand tabs etc?
  536.  * Notes:   To make life easier for the programmer, the block is first
  537.  *           read to the end of the text buffer, then moved by its size
  538.  *           so that it will not be overwritten when the main text is
  539.  *           shifted to make room for the block.
  540.  */
  541. void block_read(window, fixup)
  542. windows *window;
  543. int fixup;
  544. {
  545.     text_ptr source;        /* source for block moves */
  546.     text_ptr dest;          /* destination for block moves */
  547.     long number;            /* number of characters for block moves */
  548.     int len;                /* length (usually of current line) */
  549.     int add;                /* characters being added from another line */
  550.     long block_len;         /* length of the block */
  551.     text_ptr orig;          /* cursor location in text */
  552.     text_ptr block_text;    /* place block is temporarily moved to */
  553.     text_ptr block_dest;    /* place block must finish up in */
  554.     char num_str[MAX_COLS]; /* tab interval as a character string */
  555.     int old_tab;            /* previous tab interval */
  556.  
  557.     /*
  558.      * if cursor was beyond the end of the actual line, then add
  559.      *  padding first
  560.      */
  561.     prepare_block(window);
  562.  
  563.     /*
  564.      * find out which file to read
  565.      */
  566.     if (get_name("File to read: ", 1, g_status.rw_name) != OK) {
  567.         return;
  568.     }
  569.  
  570.     /*
  571.      * set up tab interval to use for read
  572.      */
  573.     old_tab = g_status.tab_size;
  574.     if (fixup) {
  575.         for (;;) {
  576.             strcpy(num_str, "8");
  577.             if (get_name("Tab interval: ", 1, num_str) != OK) {
  578.                 return;
  579.             }
  580.             g_status.tab_size = atoi(num_str);
  581.             if (g_status.tab_size < MAX_COLS/2) {
  582.                 break;
  583.             }
  584.         }
  585.     }
  586.  
  587.     /*
  588.      * read in the file at the end of the current main text
  589.      */
  590.     block_text = g_status.end_mem;
  591.     if (load_file(g_status.rw_name, fixup) != OK) {
  592.         /*
  593.          * make sure tab interval is restored before aborting
  594.          */
  595.         g_status.tab_size = old_tab;
  596.         return;
  597.     }
  598.  
  599.     /*
  600.      * now OK to restore original tab interval.
  601.      */
  602.     g_status.tab_size = old_tab;
  603.  
  604.     /*
  605.      * work out how long the block is
  606.      */
  607.     block_len = g_status.temp_end - block_text;
  608.  
  609.     /*
  610.      * now check that no lines will become too long as a result of
  611.      *  the block move
  612.      */
  613.     add = linelen(block_text);
  614.     if (block_text[add] == '\n') {
  615.         ++add;
  616.     }
  617.     if (window->ccol + add >= BUFF_SIZE) {
  618.         error(WARNING, "line would be too long");
  619.         return;
  620.     }
  621.     len = linelen(window->cursor);
  622.     add = prelinelen(block_text + block_len);
  623.     if (len - window->ccol + add >= BUFF_SIZE) {
  624.         error(WARNING, "line would be too long");
  625.         return;
  626.     }
  627.  
  628.     orig = window->cursor + window->ccol;
  629.  
  630.     /*
  631.      * check there is enough memory for the extra copy of
  632.      *  the block
  633.      */
  634.     if (g_status.end_mem + 2*block_len >= g_status.max_mem) {
  635.         error(WARNING, "not enough memory for read");
  636.         return;
  637.     }
  638.  
  639.     /*
  640.      * make copy of block after where text will finally end
  641.      */
  642.     source = block_text;
  643.     block_text += block_len;
  644.     hw_move(block_text, source, block_len);
  645.  
  646.     /*
  647.      * move text to make room for block
  648.      */
  649.     source = orig;
  650.     dest = source + block_len;
  651.     block_dest = source;
  652.     number = g_status.end_mem - source;
  653.     hw_move(dest, source, number);
  654.  
  655.     /*
  656.      * put block in the hole
  657.      */
  658.     hw_move(block_dest, block_text, block_len);
  659.  
  660.     /*
  661.      * fix up affected markers
  662.      */
  663.     fix_marks(window, orig, block_len);
  664.  
  665.     /*
  666.      * the block just read becomes the current block, and is visible
  667.      */
  668.     window->file_info->marker[START_BLOCK] = block_dest;
  669.     window->file_info->marker[END_BLOCK] = block_dest + block_len;
  670.     window->file_info->visible = TRUE;
  671. }
  672.  
  673. /*
  674.  * Name:    check_block
  675.  * Purpose: To check that the block is visible.
  676.  * Date:    October 1, 1989
  677.  * Passed:  window: information required to access current window
  678.  * Returns: OK if block is visible, ERROR otherwise
  679.  * Notes:   This module reports the error to the user, so there is no
  680.  *           need to do this after an error is returned.
  681.  */
  682. int check_block(window)
  683. windows *window;
  684. {
  685.     if (!window->file_info->visible) {
  686.         error(WARNING, "no visible block");
  687.         return ERROR;
  688.     }
  689.  
  690.     un_copy_line(window);
  691.  
  692.     if (window->file_info->marker[END_BLOCK] <
  693.             window->file_info->marker[START_BLOCK]) {
  694.         error(WARNING, "block end before start");
  695.         return ERROR;
  696.     }
  697.  
  698.     return OK;
  699. }
  700.  
  701. /*
  702.  * Name:    block_delete
  703.  * Purpose: To delete the currently marked block.
  704.  * Date:    October 1, 1989
  705.  * Passed:  window: information required to access current window
  706.  */
  707. void block_delete(window)
  708. windows *window;
  709. {
  710.     text_ptr source;        /* source for block moves */
  711.     text_ptr dest;          /* destination for block moves */
  712.     long number;            /* number of characters for block moves */
  713.     int len;                /* length (usually of current line) */
  714.     int add;                /* characters being added from another line */
  715.     long block_len;         /* length of the block */
  716.     text_ptr orig;          /* cursor location in text */
  717.     text_ptr next;          /* next line after end of block */
  718.  
  719.     /*
  720.      * check something there to delete
  721.      */
  722.     if (check_block(window) != OK) {
  723.         return;
  724.     }
  725.  
  726.     /*
  727.      * work out how much to delete
  728.      */
  729.     block_len = window->file_info->marker[END_BLOCK] -
  730.             window->file_info->marker[START_BLOCK];
  731.  
  732.     /*
  733.      * work out cursor position in actual text
  734.      */
  735.     if ((len = linelen(window->cursor)) > window->ccol) {
  736.         len = window->ccol;
  737.     }
  738.     orig = window->cursor + len;
  739.  
  740.     /*
  741.      * now check that no lines will become too long as a result of
  742.      *  the block move
  743.      */
  744.     add = linelen(window->file_info->marker[END_BLOCK]);
  745.     if (window->file_info->marker[END_BLOCK][add] == '\n') {
  746.         ++add;
  747.     }
  748.     if (prelinelen(window->file_info->marker[START_BLOCK]) + add >=
  749.             BUFF_SIZE) {
  750.         error(WARNING, "line would be too long");
  751.         return;
  752.     }
  753.  
  754.     /*
  755.      * adjust the cursor line
  756.      */
  757.     if (orig <= window->file_info->marker[START_BLOCK]) {
  758.         /*
  759.          * cursor was before the block, so no problems
  760.          */
  761.         ;
  762.     }
  763.     else if ((next = find_next(window->file_info->marker[END_BLOCK])) &&
  764.             orig >= next) {
  765.         /*
  766.          * cursor was after the last line of the block, so merely
  767.          *  adjust by the number of characters deleted
  768.          */
  769.         window->cursor -= block_len;
  770.     }
  771.     else {
  772.         /*
  773.          * complications! The location of the cursor has been
  774.          *  affected by the delete. The cursor line will become
  775.          *  the line the block started on.
  776.          */
  777.         source = window->file_info->marker[START_BLOCK];
  778.         source -= prelinelen(source);
  779.         window->cursor = source;
  780.         if (orig >= window->file_info->marker[END_BLOCK]) {
  781.             /*
  782.              * cursor was on the last line of the block, after the
  783.              *  end of the block. Leave cursor on the same character.
  784.              */
  785.             window->ccol +=
  786.                     prelinelen(window->file_info->marker[START_BLOCK]) -
  787.                     prelinelen(window->file_info->marker[END_BLOCK]);
  788.         }
  789.         else {
  790.             /*
  791.              * the cursor was inside the block. Leave cursor on what
  792.              *  used to be the first character of the block
  793.              */
  794.             window->ccol =
  795.                     prelinelen(window->file_info->marker[START_BLOCK]);
  796.         }
  797.     }
  798.  
  799.     /*
  800.      * move the text to delete the block
  801.      */
  802.     source = window->file_info->marker[END_BLOCK];
  803.     dest = window->file_info->marker[START_BLOCK];
  804.     number = g_status.end_mem - source;
  805.     hw_move(dest, source, number);
  806.  
  807.     /*
  808.      * fix affected markers and adjust the total text size
  809.      */
  810.     fix_marks(window, window->file_info->marker[START_BLOCK], -block_len);
  811. }
  812.  
  813. /*
  814.  * Name:    block_indent
  815.  * Purpose: To indent every line in the current block by the current tab
  816.  *           size.
  817.  * Date:    October 1, 1989
  818.  * Passed:  window: information required to access current window
  819.  */
  820. void block_indent(window)
  821. windows *window;
  822. {
  823.     text_ptr source;        /* source for block moves */
  824.     text_ptr dest;          /* destination for block moves */
  825.     long number;            /* number of characters to be moved */
  826.     text_ptr p;             /* current position within block */
  827.     text_ptr last_p;        /* end of moved block */
  828.     int i;                  /* counter for inserting indent space */
  829.     int increase;           /* total increase for indenting all lines */
  830.     int count;              /* number of chars in current line */
  831.     text_ptr new_cursor;    /* where the cursor line might finish */
  832.  
  833.     /*
  834.      * check block is OK
  835.      */
  836.     if (check_block(window) != OK) {
  837.         return;
  838.     }
  839.  
  840.     /*
  841.      * work out how much extra space we need - one indent at
  842.      *  the start of the block, then another after every \n,
  843.      *  except that if the last \n is the last character in
  844.      *  the block, we won't indent the next line.
  845.      */
  846.     increase = g_status.tab_size;
  847.     count = prelinelen(window->file_info->marker[START_BLOCK]);
  848.     for (p=window->file_info->marker[START_BLOCK];
  849.             p < window->file_info->marker[END_BLOCK]; ) {
  850.         if (*p++ == '\n') {
  851.             count = 0;
  852.             if (p < window->file_info->marker[END_BLOCK]) {
  853.                 increase += g_status.tab_size;
  854.             }
  855.         }
  856.         if (++count >= BUFF_SIZE - g_status.tab_size) {
  857.             error(WARNING, "line too long");
  858.             return;
  859.         }
  860.     }
  861.  
  862.     /*
  863.      * move everything from the start of the block down to make
  864.      *  room for inserted characters
  865.      */
  866.     source = window->file_info->marker[START_BLOCK];
  867.     dest = source + increase;
  868.     number = g_status.end_mem - source;
  869.     if (g_status.end_mem + increase >= g_status.max_mem) {
  870.         error(WARNING, "buffer full");
  871.         return;
  872.     }
  873.     hw_move(dest, source, number);
  874.  
  875.     /*
  876.      * start from the copy of the block, moving text into position, and
  877.      *  keeping track of where the cursor will finish
  878.      *
  879.      * the start of the block is handled as a special case, since it
  880.      *  will not necessarily be the start of a line
  881.      */
  882.     new_cursor = window->cursor + increase;
  883.     if (window->file_info->marker[START_BLOCK] -
  884.             prelinelen(window->file_info->marker[START_BLOCK]) ==
  885.             window->cursor) {
  886.         /*
  887.          * the cursor was on the first line of the block
  888.          */
  889.         new_cursor = source;
  890.         if (window->cursor + window->ccol >=
  891.                 window->file_info->marker[START_BLOCK]) {
  892.             window->ccol += g_status.tab_size;
  893.             if (window->ccol >= g_display.ncols) {
  894.                 window->ccol = g_display.ncols-1;
  895.             }
  896.         }
  897.     }
  898.  
  899.     /*
  900.      * note where the block now finishes
  901.      */
  902.     last_p = window->file_info->marker[END_BLOCK] + increase;
  903.  
  904.     /*
  905.      * markers must be adjusted separately for each line, so that
  906.      *  any markers within the block stay in their correct places
  907.      */
  908.     fix_marks(window, source, (long) g_status.tab_size);
  909.  
  910.     /*
  911.      * put in the first indent
  912.      */
  913.     for (i=0; i < g_status.tab_size; i++) {
  914.         *source++ = ' ';
  915.     }
  916.  
  917.     /*
  918.      * now process the rest of the block
  919.      *
  920.      * Note the text is being copied from dest to source! This is rather
  921.      *  confusing, a consequence of the direction of the previous
  922.      *  block move. I should probably have used different variable
  923.      *  names...
  924.      */
  925.     for (;;) {
  926.         if (source >= dest) { /* finished */
  927.             break;
  928.         }
  929.         if (*dest == '\n') {
  930.             /*
  931.              * copy the character itself
  932.              */
  933.             *source++ = *dest++;
  934.  
  935.             /*
  936.              * keep track of the cursor position if it was inside
  937.              *  the block
  938.              */
  939.             if (dest == new_cursor) {
  940.                 new_cursor = source;
  941.                 window->ccol += g_status.tab_size;
  942.                 if (window->ccol >= g_display.ncols) {
  943.                     window->ccol = g_display.ncols-1;
  944.                 }
  945.             }
  946.  
  947.             /*
  948.              * avoid indenting the line following the end of the
  949.              *  block
  950.              */
  951.             if (dest >= last_p) {
  952.                 break;
  953.             }
  954.  
  955.             /*
  956.              * fix the affected marks and add the indentation
  957.              */
  958.             fix_marks(window, source, (long) g_status.tab_size);
  959.             for (i=0; i < g_status.tab_size; i++) {
  960.                 *source++ = ' ';
  961.             }
  962.         }
  963.         else {
  964.             *source++ = *dest++;
  965.         }
  966.     }
  967.  
  968.     /*
  969.      * update the cursor line if necessary
  970.      */
  971.     if (window->cursor > window->file_info->marker[START_BLOCK]) {
  972.         window->cursor = new_cursor;
  973.     }
  974. }
  975.  
  976. /*
  977.  * Name:    block_unindent
  978.  * Purpose: To unindent every line in the current block by the current tab
  979.  *           size.
  980.  * Date:    October 1, 1989
  981.  * Passed:  window: information required to access current window
  982.  * Notes:   Each line is first checked to see that it has the required
  983.  *           number of leading blank spaces.
  984.  */
  985. void block_unindent(window)
  986. windows *window;
  987. {
  988.     text_ptr source;        /* source for block moves */
  989.     text_ptr dest;          /* destination for block moves */
  990.     long number;            /* number of characters to be moved */
  991.     text_ptr p;             /* current position within block */
  992.     int i;                  /* counter for removing indent space */
  993.     int decrease;           /* total decrease for unindenting all lines */
  994.     text_ptr new_cursor;    /* where the cursor line might finish */
  995.     text_ptr orig_end_mem;  /* old end of main text (for fixups) */
  996.     text_ptr orig_end_block;/* old end of marked block (for fixups */
  997.     int len;                /* length of current line or tab interval */
  998.  
  999.     /*
  1000.      * check block OK
  1001.      */
  1002.     if (check_block(window) != OK) {
  1003.         return;
  1004.     }
  1005.  
  1006.     /*
  1007.      * save info for fixups later
  1008.      */
  1009.     orig_end_mem = g_status.end_mem;
  1010.     orig_end_block = window->file_info->marker[END_BLOCK];
  1011.  
  1012.     /*
  1013.      * work out how much space we need to remove - one indent at
  1014.      *  the start of the block, then another after every \n,
  1015.      *  except that if the last \n is the last character in
  1016.      *  the block, we won't unindent that line.
  1017.      */
  1018.     decrease = 0;
  1019.     for (p=window->file_info->marker[START_BLOCK];
  1020.             p < window->file_info->marker[END_BLOCK]; ) {
  1021.         if (p == window->file_info->marker[START_BLOCK] || *p++ == '\n') {
  1022.             if (p < window->file_info->marker[END_BLOCK]) {
  1023.                 /*
  1024.                  * check that the required blank space is available
  1025.                  */
  1026.                 for (i=0; i < g_status.tab_size; i++) {
  1027.                     if (p[i] != ' ') {
  1028.                         if (p[i] != '\n') {
  1029.                             error(WARNING,
  1030.                                     "line with too few leading spaces");
  1031.                             return;
  1032.                         }
  1033.                         else {
  1034.                             /*
  1035.                              * blank lines can be safely unindented
  1036.                              */
  1037.                             break;
  1038.                         }
  1039.                     }
  1040.                     else {
  1041.                         decrease++;
  1042.                     }
  1043.                 }
  1044.             }
  1045.             if (p == window->file_info->marker[START_BLOCK]) {
  1046.                 ++p;
  1047.             }
  1048.         }
  1049.     }
  1050.  
  1051.     /*
  1052.      * fix cursor position if on first line of block
  1053.      */
  1054.     new_cursor = window->cursor;
  1055.     if (window->file_info->marker[START_BLOCK] -
  1056.             prelinelen(window->file_info->marker[START_BLOCK]) ==
  1057.             window->cursor) {
  1058.         if (window->cursor + window->ccol >=
  1059.                 window->file_info->marker[START_BLOCK]) {
  1060.             window->ccol -= g_status.tab_size;
  1061.             if (window->ccol <= 0) {
  1062.                 window->ccol = 0;
  1063.             }
  1064.         }
  1065.     }
  1066.  
  1067.     /*
  1068.      * move text into new position
  1069.      *
  1070.      * first line is a special case, since the block will not necessarily
  1071.      *  start at the start of a line
  1072.      */
  1073.     source = dest = window->file_info->marker[START_BLOCK];
  1074.     if ((len=linelen(source)) > g_status.tab_size) {
  1075.         len = g_status.tab_size;
  1076.     }
  1077.     fix_marks(window, dest, -(long)len);
  1078.     source += len; /* simply ignore spaces */
  1079.  
  1080.     /*
  1081.      * now process the rest of the lines
  1082.      */
  1083.     for (;;) {
  1084.         if (source >= orig_end_block) {
  1085.             break;
  1086.         }
  1087.         if (*source == '\n') {
  1088.             *dest++ = *source++;
  1089.             if (source == new_cursor) {
  1090.                 new_cursor = dest;
  1091.                 window->ccol -= g_status.tab_size;
  1092.                 if (window->ccol <= 0) {
  1093.                     window->ccol = 0;
  1094.                 }
  1095.             }
  1096.             if (source >= orig_end_block) {
  1097.                 break;
  1098.             }
  1099.             if ((len=linelen(source)) > g_status.tab_size) {
  1100.                 len = g_status.tab_size;
  1101.             }
  1102.             fix_marks(window, dest, -(long)len);
  1103.             source += len; /* simply ignore it */
  1104.         }
  1105.         else {
  1106.             *dest++ = *source++;
  1107.         }
  1108.     }
  1109.  
  1110.     /*
  1111.      * move everything from the end of the unindented block up to
  1112.      *  close the gap
  1113.      */
  1114.     number = orig_end_mem - source;
  1115.     hw_move(dest, source, number);
  1116.  
  1117.     /*
  1118.      * adjust the cursor position if necessary
  1119.      */
  1120.     if (window->cursor > window->file_info->marker[START_BLOCK]) {
  1121.         if (window->cursor > orig_end_block) {
  1122.             window->cursor -= decrease;
  1123.         }
  1124.         else {
  1125.             window->cursor = new_cursor;
  1126.         }
  1127.     }
  1128. }
  1129.  
  1130. /*
  1131.  * Name:    block_write
  1132.  * Purpose: To write the currently marked block to a disk file.
  1133.  * Date:    October 1, 1989
  1134.  * Passed:  window: information required to access current window
  1135.  * Notes:   If the file already exists, the user gets to choose whether
  1136.  *           to overwrite or append.
  1137.  */
  1138. void block_write(window)
  1139. windows *window;
  1140. {
  1141.     /*
  1142.      * make sure block is marked OK
  1143.      */
  1144.     if (check_block(window) != OK) {
  1145.         return;
  1146.     }
  1147.  
  1148.     /*
  1149.      * find out which file to write to
  1150.      */
  1151.     if (get_name("Filename: ", 1, g_status.rw_name) != OK) {
  1152.         return;
  1153.     }
  1154.  
  1155.     /*
  1156.      * if the file exists, find out whether to overwrite or append
  1157.      */
  1158.     if (hw_fattrib(g_status.rw_name) != ERROR) {
  1159.         set_prompt("File exists. Overwrite or Append? (o/a): ", 1);
  1160.         switch (display(get_oa, 1)) {
  1161.         case A_OVERWRITE:
  1162.             hw_unlink(g_status.rw_name);
  1163.             break;
  1164.         case A_APPEND:
  1165.             error(TEMP, "appending block to '%s'", g_status.rw_name);
  1166.             if (hw_append(g_status.rw_name,
  1167.                     window->file_info->marker[START_BLOCK],
  1168.                     window->file_info->marker[END_BLOCK]) == ERROR) {
  1169.                 error(WARNING, "could not append block");
  1170.             }
  1171.             return;
  1172.         default:
  1173.             return;
  1174.         }
  1175.     }
  1176.  
  1177.     error(TEMP, "writing block to '%s'", g_status.rw_name);
  1178.     if (hw_save(g_status.rw_name,
  1179.             window->file_info->marker[START_BLOCK],
  1180.             window->file_info->marker[END_BLOCK]) == ERROR) {
  1181.         error(WARNING, "could not write block");
  1182.     }
  1183. }
  1184.  
  1185. /*
  1186.  * Name:    block_print
  1187.  * Purpose: To print the currently marked block.
  1188.  * Date:    October 1, 1989
  1189.  * Passed:  window: information required to access current window
  1190.  */
  1191. void block_print(window)
  1192. windows *window;
  1193. {
  1194.     char answer[MAX_COLS];  /* entire file or just marked block? */
  1195.  
  1196.     /*
  1197.      * print entire file (WordStar) or just marked block (Turbo)?
  1198.      */
  1199.     for (;;) {
  1200.         strcpy(answer, "f");
  1201.         if (get_name("Print file or block? (f/b): ", 1, answer) != OK) {
  1202.             return;
  1203.         }
  1204.         answer[0] = tolower(answer[0]);
  1205.         if (answer[0] == 'f') {
  1206.             hw_print(window->file_info->start_text,
  1207.                     window->file_info->end_text-1); /* -1 to avoid \0 */
  1208.             return;
  1209.         }
  1210.         else if (answer[0] == 'b') {
  1211.             break;
  1212.         }
  1213.     }
  1214.  
  1215.     /*
  1216.      * check block is marked OK
  1217.      */
  1218.     if (check_block(window) != OK) {
  1219.         return;
  1220.     }
  1221.  
  1222.     /*
  1223.      * write it to the printer
  1224.      */
  1225.     error(TEMP, "Printing block...");
  1226.     if (hw_print(window->file_info->marker[START_BLOCK],
  1227.             window->file_info->marker[END_BLOCK])) {
  1228.         error(WARNING, "could not print block");
  1229.     }
  1230. }
  1231.